package twoD.hofem.Q;

import inf.jlinalg.Vector2D;
import twoD.hofem.FunctionRToRn;
import twoD.hofem.Interval;

/**
 * An EdgeFunction is a function [-1, 1] -> R^2 for which the start and the end
 * point can be specified.
 * 
 * @author M. Baitsch
 */
public abstract class EdgeFunction implements FunctionRToRn {

	/** Tolerance for getCommonPoint and isEndpoint */
	private static final double EPS = 1e-13;

	/**
	 * Tests if two points are coincident with respect to a specified tolerance.
	 * 
	 * @param p1
	 *            first point
	 * @param p2
	 *            second point
	 * @param eps
	 *            tolerance
	 * @return <code>true</code> if points coincide, <code>false</code>
	 *         otherwise
	 */
	private static boolean coincident(double[] p1, double[] p2, double eps) {
		double d1 = Math.abs(p1[0] - p2[0]);
		if (d1 > eps) {
			return false;
		}
		double d2 = Math.abs(p1[1] - p2[1]);
		if (d2 > eps) {
			return false;
		}
		return true;
	}

	/** Start and end points */
	private Vector2D[] pts_ = new Vector2D[2];

	/**
	 * Returns the start or end point this EdgeFunction has in common with the
	 * specified EdgeFunction.
	 * 
	 * @param ef
	 *            other edge function
	 * @return common start or end point or <code>null</code>, if the two
	 *         edge function do not have a common point
	 */
	public Vector2D getCommonPoint(EdgeFunction ef) {
		double[] p11 = valueAt(-1.0);
		double[] p12 = valueAt(1.0);
		double[] p21 = ef.valueAt(-1.0);
		double[] p22 = ef.valueAt(1.0);

		if (coincident(p11, p21, EPS) || coincident(p11, p22, EPS)) {
			return new Vector2D(p11);
		}
		if (coincident(p12, p21, EPS) || coincident(p12, p22, EPS)) {
			return new Vector2D(p12);
		}
		return null;
	}

	public final Interval getDomain() {
		return new Interval(-1, 1);
	}

	/**
	 * Returns the end point of this edge function.
	 * 
	 * @return end point
	 */
	public Vector2D getEndPoint() {
		return pts_[1];
	}

	public int getN() {
		return 2;
	}

	/**
	 * Returns the start point of this edge function.
	 * 
	 * @return start point
	 */
	public Vector2D getStartPoint() {
		return pts_[0];
	}

	/**
	 * Lets the edge function update its internal state. Invoked after the start
	 * and end points have been set.
	 */
	protected abstract void initialize();

	/**
	 * Tests if the specified point is the end point of this EdgeFunction.
	 * 
	 * @param pt
	 *            point to test for
	 * @return <code>true</code> if pt is the end point, <code>false</code>
	 *         otherwise
	 */
	public boolean isEndPoint(Vector2D pt) {
		return coincident(valueAt(1), pt.toArray(), EPS);
	}

	/**
	 * Sets the start end end points of this edge function.
	 * 
	 * @param p1
	 *            start point
	 * @param p2
	 *            end point
	 */
	public void setPoints(Vector2D p1, Vector2D p2) {
		pts_[0] = p1;
		pts_[1] = p2;
		initialize();
	}
}
